package com.lily.aidouzixun.ui;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;

import com.lily.aidouzixun.R;
import com.lily.aidouzixun.util.L;

/**
 * 下拉刷新ListView
 *
 */
public class PullRefreshableListView extends LinearLayout implements OnTouchListener{
	private static final String TAG = "PullRefreshableListView";
	/**
	 * 用于存储上次刷新事件
	 */
	private SharedPreferences preferences;
	
	/**
	 * 下拉头的View
	 */
	private  View header;
	
	/**
	 * 刷新时显示的进度条
	 */
	private ProgressBar progressBar;
	
	/**
	 * 下拉的箭头
	 */
	private ImageView arrow;
	
	/**
	 * 下拉描述
	 */
	private TextView description;
	
	/**
	 * 最近更新事件
	 */
	private TextView updateAt;
	
	/**
	 * 判定滑动之前，手指可以移动的最大距离
	 */
	private int touchSlop;
	
	/**
	 * 上次更新事件的毫秒值
	 */
	private long lastUpdateTime;
	
	/**
	 * 上次更新事件的字符串常量，用于作为sharedPreference的键值
	 */
	public static final String UPDATE_AT = "update_at";
	
	/**
	 * 为了防止不同界面的下拉刷新更新时间上相互冲突，使用id来区分
	 */
	private static int mId = -1;
	/**
	 * 判断上次刷新时间
	 */
	private static final long ONE_MINUTE = 60 * 1000;
	private static final long ONE_HOUR = 60 * ONE_MINUTE;
	private static final long ONE_DAY = 24 * ONE_HOUR;
	private static final long ONE_MONTH = 30 * ONE_DAY;
	private static final long ONE_YEAR = 12 * ONE_MINUTE;
	/**
	 * 是否已经加载过一次layout,这里的onLayout中的初始化只需要加载一次
	 */
	private boolean loadOnce;
	/**
	 * 下拉头的高度
	 */
	private int hideHeaderHeight;
	/**
	 * 下拉头的布局参数
	 */
	private MarginLayoutParams headerLayoutParams;
	/**
	 * 需要下拉刷新的ListView
	 */
	private ListView listView;
	/**
	 * 需要下拉刷新的ScrollView
	 */
	private ScrollView scrollView;
	/**
	 * 当前是否可以下拉，只有ListView滚动到头的时候，才允许下拉
	 */
	private boolean ableToPull;
	/**
	 * 手指按下时的屏幕坐标
	 */
	private float yDown;
	/**
	 * 下拉状态 
	 */
	private static final int  STATUS_PULL_TO_REFRESH = 0;
	/**
	 * 释放立即刷新状态
	 */
	private static final int STATUS_RELEASE_TO_REFRESH = 1;
	/**
	 * 正在刷新状态
	 */
	private static final int STATUS_REFRESHING = 2;
	/**
	 * 刷新完成或未刷新状态
	 */
	private static final int STATUS_REFRESH_FINISHED = 3;
	/**
	 * 当前处理状态，可选值有STATUS_PULL_TO_REFRESH、STATUS_RELEASE_TO_REFRESH,
	 * STATUS_REFRESHING和STATUS_REFRESH_FINISHED
	 */
	private int currentStatus = STATUS_REFRESH_FINISHED;
	/**
	 * 记录上一次状态，避免进行重复操作
	 */
	private int lastStatus = currentStatus;
	/**
	 * 下拉头回滚的速度
	 */
	private static final int SCROLL_SPEED = -20;
	/**
	 *下拉刷新的回调接口 
	 */
	private PullToRefreshListener mListener;
	
	
	
	/**
	 * 下拉刷新控件的构造函数，会在运行时动态增加一个下拉头的布局
	 * @param context
	 * @param attrs
	 */
	public PullRefreshableListView(Context context,AttributeSet attrs){
		super(context,attrs);
		//得到下拉头上的布局组件
		preferences = PreferenceManager.getDefaultSharedPreferences(context);
		header = LayoutInflater.from(context).inflate(R.layout.pull_to_refresh,null);
		progressBar = (ProgressBar)header.findViewById(R.id.progress_bar);
		arrow = (ImageView)header.findViewById(R.id.arrow);
		description = (TextView)header.findViewById(R.id.description);
		updateAt = (TextView) header.findViewById(R.id.updated_at);
		//得到不产生滑动，手指移动的最大距离
		touchSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 25;
		Log.v(TAG, "touchSlop-->"+touchSlop);
		//刷新下拉头中上次更新的时间的文字描述
		refershUpdatedAtValue();
		//设置方向
		setOrientation(VERTICAL);
		//把header加到第一个位置，ListView则在第二个位置
		addView(header, 0);
	}
	
	/**
	 * 刷新下拉头上次更新事件的文字描述
	 */
	private void refershUpdatedAtValue(){
		//得到上次刷新的时间
		lastUpdateTime = preferences.getLong(UPDATE_AT + mId, -1);
		long currentTime = System.currentTimeMillis();
		long timePassed = currentTime - lastUpdateTime;
		String updateAtValue;
		if(lastUpdateTime == -1){	//第一次刷新
			updateAtValue = "暂未更新过";
		}else if(timePassed < 0){	//时间 有问题
			updateAtValue = "时间有问题";
		}else if(timePassed < ONE_MINUTE){	//刚刷新小于1分钟
			updateAtValue = "刚刚刷新过";
		}else{
			updateAtValue = "最近更新："+lastUpdateTime;
		}
		updateAt.setText(updateAtValue);
	}
	
	
	/**
	 * 进行一些关键性的初始化操作，比如：将下拉头向上偏移进行隐藏，给ListView注册onTouch事件
	 */
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		if(changed && !loadOnce){	//这里的初始化，只需要一次
			//下拉头的高度
			hideHeaderHeight = -header.getHeight();
			Log.v(TAG, "hideHeaderHeight-->"+hideHeaderHeight);
			//下拉头的布局参数
			headerLayoutParams = (MarginLayoutParams)header.getLayoutParams();
			headerLayoutParams.topMargin = hideHeaderHeight;
			//得到需要下拉刷新的ListView
			for (int i =0; i < getChildCount(); i++) {
				View view = getChildAt(i);
				if (view instanceof ListView) {
					listView = (ListView)view;
				} else if (view instanceof ScrollView){
					scrollView = (ScrollView) view;
					LinearLayout linearLayout = (LinearLayout) scrollView.getChildAt(0);
					for (int j = 0; j < linearLayout.getChildCount(); j++) {
						View childView = linearLayout.getChildAt(j);
						if (childView instanceof ListView) {
							listView = (ListView)childView;
						}
					}
				}
			}
			//设置触摸事件，如果有scrollView,则设置
			if (scrollView == null) {
				listView.setOnTouchListener(this);
			} else {
				scrollView.setOnTouchListener(this);
			}
			//设置加载过一次了
			loadOnce = true;
		}
	}
	
	/**
	 * 给下拉刷新控件注册一个监听器
	 * @param listener 监听器的实现
	 * @param id 为了防止不同界面的下拉刷新在上次更新时间上互相有冲突，请不同界面在注册下拉刷新监听器时一定要传入不同的id
	 */
	public void setOnRefreshListener(PullToRefreshListener listener,int id){
		mListener = listener;
		mId = id;
	}
	
	/**
	 *当所有的刷新逻辑完成以后，记住调用一下该方法，否则你的ListView将一直处于正在刷新状态 
	 */
	public void finishRefreshing(){
		currentStatus = STATUS_REFRESH_FINISHED;
		//设置刷新时间，供下次使用
		preferences.edit().putLong(UPDATE_AT + mId, System.currentTimeMillis()).commit();
		new HideHeaderTask().execute();
	}
	
	/**
	 * 当ListView被触摸时调用，其中处理了各种下拉刷新的具体逻辑
	 */
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		Log.v(TAG, "onTouch");
		//设置是滚动还是下拉
		setIsAbleToPull(event);
		if(ableToPull){	//如果可以下拉刷新
			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:	//按下
				Log.v(TAG, "ACTION_DOWN");
				yDown = event.getRawY();	//得到按下处的纵坐标
				break;
			case MotionEvent.ACTION_MOVE:	//移动
				Log.v(TAG, "ACTION_MOVE");
				float yMove = event.getRawY();	//滑动过后的纵坐标
				int distance = (int) (yMove - yDown);	//滑动的距离
				L.v(TAG, "distance-->" + distance + "--touchSlop-->" + touchSlop);
				//如果手指是上滑状态，并且下拉头是完全隐藏的，就屏蔽下拉事件
				if(distance <= 0 && headerLayoutParams.topMargin <= hideHeaderHeight){
				//if(distance <= 0){
					Log.v(TAG, "distance <= 0 && headerLayoutParams.topMargin <= hideHeaderHeight");
					return false;
				}
				//如果滑动的距离不够产生滑动，屏蔽下拉事件
				if(distance < touchSlop){
					Log.v(TAG, "distance < touchSlop");
					return false;
				}
				//如果当前状态不是刷新状态
				if(currentStatus != STATUS_REFRESHING){
					if(headerLayoutParams.topMargin > 0 ){	
						//当下拉头顶部离开顶部时，设置为释放下拉状态
						currentStatus = STATUS_RELEASE_TO_REFRESH;
					}else{
						//当下拉头顶部没有离开顶部时，设置为下拉状态
						currentStatus = STATUS_PULL_TO_REFRESH;
					}
					//设置偏移下拉头的topMargin值来实现下拉效果
					headerLayoutParams.topMargin = (distance / 2) + hideHeaderHeight;
					header.setLayoutParams(headerLayoutParams);
				}
				break;
			case MotionEvent.ACTION_UP:	//手势抬起，和default放在一起
				Log.v(TAG, "ACTION_UP");
			default:
				if(currentStatus == STATUS_RELEASE_TO_REFRESH){
					//如果松手时是释放立即刷新状态，就去调用正在刷新的任务
					new RefreshingTask().execute();
				}else if(currentStatus == STATUS_PULL_TO_REFRESH){
					//如果松手时是下拉状态，就去调用隐藏下拉头的任务
					new HideHeaderTask().execute();
				}
				break;
			}
			
			//时刻记得更新下拉头中的信息,如果当前正处于下拉或释放状态
			if(currentStatus == STATUS_PULL_TO_REFRESH || currentStatus == STATUS_RELEASE_TO_REFRESH){
				Log.v(TAG, "下拉或释放状态，屏蔽listView上的事件，status code->"+currentStatus);
				//更新头部信息
				updateHeaderView();
				//要让ListView失去焦点，否则被电击的那一项会一直处于选择状态
				if (scrollView == null) {
					listView.setPressed(false);
					listView.setFocusable(false);
					listView.setFocusableInTouchMode(false);
				} else {
					scrollView.setPressed(false);
					scrollView.setFocusable(false);
					scrollView.setFocusableInTouchMode(false);
				}

				//保持最近的状态
				lastStatus  = currentStatus;
				//当前正处于下拉或释放状态，通过返回true屏蔽掉ListView的滚动事件
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 根据当前ListView的滚动状态来设置ableToPull的值
	 * 也有可能是ScrollView
	 * 每次都需要在onTouch中第一行执行，这样可以判断出当前应该是滚动ListView还是下拉ListView
	 * @param event
	 */
	private void setIsAbleToPull(MotionEvent event) {
		// TODO Auto-generated method stub
		//得到ListView的第一个条目
		View firstChild = null;
		if (scrollView == null) {
			firstChild = listView.getChildAt(0);
		} else {
			firstChild = scrollView.getChildAt(0);
		}

		if(firstChild != null){
			//第一个条目的位置
			int firstVisiblPos = listView.getFirstVisiblePosition();
			//if(firstVisiblPos == 0 && firstChild.getTop() == 0){
			if(firstChild.getTop() == 0 && firstChild.getTop() == 0){
				if(!ableToPull){
					//得到手指按下时的纵坐标
					yDown = event.getRawY();
				}
				//如果首个元素的上边缘距离父布局值为0，即证明Listview滚动到了最顶端，可以下拉刷新
				ableToPull = true;
			}else{	//如果ListView首个条目不是在最上面，则不能下拉刷新，并改变布局
				if(headerLayoutParams.topMargin != hideHeaderHeight){
					headerLayoutParams.topMargin = hideHeaderHeight;
					header.setLayoutParams(headerLayoutParams);
					ableToPull = false;
				}
			}
		}else{	//如果没有元素，也可以下拉刷新
			ableToPull = true;
		}
	}
	/**
	 * 更新下拉头中的信息
	 */
	private void updateHeaderView(){
		if(lastStatus != currentStatus){	//如果当前操作与上次不一样
			if(currentStatus == STATUS_PULL_TO_REFRESH){
				//如果是下拉状态
				description.setText("下拉可以刷新");
				arrow.setVisibility(View.VISIBLE);
				progressBar.setVisibility(View.GONE);
				rotateArrow();
			}else if(currentStatus == STATUS_RELEASE_TO_REFRESH){
				//释放刷新状态
				description.setText("松开可以刷新");
				arrow.setVisibility(View.VISIBLE);
				progressBar.setVisibility(View.GONE);
				rotateArrow();
			}else if(currentStatus == STATUS_REFRESHING){
				//正在刷新
				description.setText("正在刷新");
				progressBar.setVisibility(View.VISIBLE);
				arrow.clearAnimation();
				arrow.setVisibility(View.GONE);
			}
			//更新上次更新时间
			refershUpdatedAtValue();
		}
		
	}
	/**
	 * 根据当前的状态来旋转箭头
	 */
	private void rotateArrow(){
		float pivotX = arrow.getWidth() / 2f;
		float pivotY = arrow.getHeight() / 2f;
		float fromDegrees = 0f;
		float toDegrees = 0f;
		if(currentStatus == STATUS_PULL_TO_REFRESH){
			//下拉
			fromDegrees = 180f;
			toDegrees = 360f;
		}else if(currentStatus == STATUS_RELEASE_TO_REFRESH){
			//下拉释放
			fromDegrees = 0f;
			toDegrees = 180f;
		}
		//设置动画
		RotateAnimation animation = new RotateAnimation(fromDegrees, toDegrees, pivotX, pivotY);
		animation.setDuration(100);	//动画持续时间为0.1s
		animation.setFillAfter(true);	//动画执行完后停留在执行完的状态
		arrow.startAnimation(animation);
	}
	/**
	 * 正在刷新的任务，在此任务中会去回调组册进来的下来刷新监听器
	 *
	 */
	class RefreshingTask extends AsyncTask<Void,Integer,Void>{
		
		@Override
		protected Void doInBackground(Void... params) {
			int topMargin = headerLayoutParams.topMargin;
			//循环改变topMargin的值
			while(true){
				topMargin = topMargin + SCROLL_SPEED;
				if(topMargin <= 0){
					topMargin = 0;
					break;
				}
				//更新下拉头界面
				publishProgress(topMargin);
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			//当前状态为正在刷新
			currentStatus = STATUS_REFRESHING;
			publishProgress(0);
			if(mListener != null){
				//调用下拉刷新回调方法
				mListener.onRefresh();
			}
			return null;
		}
		
		@Override
		protected void onProgressUpdate(Integer... topMargin) {
			updateHeaderView();
			headerLayoutParams.topMargin = topMargin[0];
			header.setLayoutParams(headerLayoutParams);
		}
	}
	/**
	 * 隐藏下拉头的任务，当为进行下拉刷新或下拉刷新完成时，此任务将会使下拉头重新隐藏
	 *
	 */
	class HideHeaderTask extends AsyncTask<Void, Integer, Integer>{

		@Override
		protected Integer doInBackground(Void... params) {
			// TODO Auto-generated method stub
			int topMargin = headerLayoutParams.topMargin;
			while(true){
				topMargin = topMargin + SCROLL_SPEED;
				if(topMargin <= hideHeaderHeight){
					topMargin = hideHeaderHeight;
					break;
				}
				publishProgress(topMargin);
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
					
			}
			return topMargin;
		}
		/**
		 * 刷新下拉头界面
		 */
		@Override
		protected void onProgressUpdate(Integer... topMargin) {
			headerLayoutParams.topMargin = topMargin[0];
			header.setLayoutParams(headerLayoutParams);
		}
		/**
		 * 隐藏下拉头之后的操作
		 */
		@Override
		protected void onPostExecute(Integer topMargin) {
			headerLayoutParams.topMargin = topMargin;
			header.setLayoutParams(headerLayoutParams);
			//当前状态为下拉刷新完成
			currentStatus = STATUS_REFRESH_FINISHED;
		}
		
	}
	
	/**
	 * 下拉刷新的监听器，使用下拉刷新的地方必须注册此监听器来获取刷新回调
	 *
	 */
	public interface PullToRefreshListener{
		
		/**
		 * 刷新时会去回调此方法，在方法内部编写具体的刷新逻辑，注意，此方法是在子线程中调用的，你可以不必开县城来进行耗时操作
		 */
		void onRefresh();
	}
}
